﻿using System;
using System.Collections.Generic;
using System.Xml.XPath;

namespace Allegro.TaskExecution
{
    /// <summary>
    /// Conditional task
    /// </summary>
    public class Conditional : Task
    {
        /// <summary>
        /// Predicate. If not set or evaluates to true, the task will run, otherwise not.
        /// An exception is thrown if the condition is not set when the task is executed
        /// </summary>
        public IExecutionCondition If { get; set; }

        /// <summary>
        /// Alternatively, the condition of the conditional can be specified by a Predicate.
        /// </summary>
        public Predicate<IExecutionContext> Condition { get; set; }

        /// <summary>
        /// If the If predicate evaluates to true, the Then task will be executed.
        /// An exception is thrown if the Then task is not set when the task is executed
        /// </summary>
        public ITask Then { get; set; }

        /// <summary>
        /// If the If predicate evaluates to false, the Else task will be executed
        /// </summary>
        public ITask Else { get; set; }

        /// <summary>
        /// Overridable method for executing a task
        /// </summary>
        /// <param name="context"></param>
        protected override void ExecuteTask(IExecutionContext context)
        {
            if(If == null && Condition == null)
            {
                string msg = "Conditional task has no condition";
                Log.Error(msg, this, LogEventTypes.TaskEvent);
                throw new ApplicationException(msg);
            }
            if(Then == null)
            {
                string msg = "Conditional task has no Then task";
                Log.Error(msg, this, LogEventTypes.TaskEvent);
                throw new ApplicationException(msg);
            }
            // Navigate to the task execution context
            IExecutionContext executionContext = GetTaskExecutionContext(this, context);

            // Check if the execution should be suspended or aborted
            CheckIfSuspendOrAbort(executionContext);

            // Pre process
            ICompositeTaskActivity compositeTaskActivity = Activity as ICompositeTaskActivity;
            if (compositeTaskActivity != null)
            {
                compositeTaskActivity.BeforeExecute(this, executionContext);
            }
            ITaskContextDerivation contextDerive = executionContext as ITaskContextDerivation;

            // Execute
            ITask task = null;
            if (If != null && If.IsTrue(executionContext) || Condition != null && Condition(executionContext))
            {
                task = Then;
            }
            else if(Else != null)
            {
                task = Else;
            }
            if(task != null)
            {
                IExecutionContext childContext = (contextDerive != null) ? contextDerive.New(task) : executionContext;
                if (task.Activity != null && compositeTaskActivity != null)
                {
                    compositeTaskActivity.PreProcessChildTask(this, task, executionContext, childContext);
                }
                ITaskContextCoordination taskCoordination = childContext as ITaskContextCoordination;
                if (taskCoordination != null)
                {
                    taskCoordination.RegisterTaskInContext(task);
                }

                // Execute child task
                task.Execute(childContext);

                if (task.Activity != null && compositeTaskActivity != null)
                {
                    compositeTaskActivity.PostProcessChildTask(this, task, executionContext, childContext);
                }
                if (task is ITaskControl && childContext is ITaskContextCoordination)
                {
                    ITaskControl control = task as ITaskControl;
                    //control.PostExecute(taskCoordination);
                    taskCoordination.Complete(task);
                }
            }

            // Post process
            if (compositeTaskActivity != null)
            {
                compositeTaskActivity.AfterExecute(this, executionContext);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="persistenceContext"></param>
        protected override void SaveContent(System.Xml.XmlWriter writer, IXmlPersistenceContext persistenceContext)
        {
            base.SaveContent(writer, persistenceContext);

            // If
            if (If != null)
            {
                writer.WriteStartElement("If", XmlNamespace);
                XmlPersist.SaveObject(writer, If, persistenceContext);
                writer.WriteEndElement();
            }
            // Then
            if(Then != null)
            {
                writer.WriteStartElement("Then", XmlNamespace);
                XmlPersist.SaveObject(writer, Then, persistenceContext);
                writer.WriteEndElement();              
            }
            // Else
            if (Else != null)
            {
                writer.WriteStartElement("Else", XmlNamespace);
                XmlPersist.SaveObject(writer, Else, persistenceContext);
                writer.WriteEndElement();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="itemNode"></param>
        /// <param name="persistenceContext"></param>
        protected override void LoadContent(XPathNavigator itemNode, IXmlPersistenceContext persistenceContext)
        {
            base.LoadContent(itemNode, persistenceContext);

            // If
            XPathNavigator ifNode = itemNode.SelectSingleNode("al:If", persistenceContext.NamespaceManager);
            if (ifNode != null)
            {
                ifNode.MoveToFirstChild();
                If = XmlPersist.LoadObject<IExecutionCondition>(ifNode, persistenceContext);
            }
            // Then
            XPathNavigator thenNode = itemNode.SelectSingleNode("al:Then", persistenceContext.NamespaceManager);
            if (thenNode != null)
            {
                thenNode.MoveToFirstChild();
                Then = XmlPersist.LoadObject<ITask>(thenNode, persistenceContext);
            }
            // Else
            XPathNavigator elseNode = itemNode.SelectSingleNode("al:Else", persistenceContext.NamespaceManager);
            if (elseNode != null)
            {
                elseNode.MoveToFirstChild();
                Else = XmlPersist.LoadObject<ITask>(elseNode, persistenceContext);
            }
        }

        internal void ProgressUpdate(ITask task, IExecutionContext context, ProgressEventType eventType, double progress, string message)
        {
            PostEvent(task, context, eventType, progress, message);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="callback"></param>
        /// <param name="context"></param>
        public override void NotifyProgress(TaskExecutionProgressNotification callback, IExecutionContext context)
        {
            base.NotifyProgress(callback, context);

            // Subscribe for events from the child task
            if (Then != null)
                Then.NotifyProgress(ProgressUpdate, context);
            if(Else != null)
                Else.NotifyProgress(ProgressUpdate, context);
        }
    }
}
